import { BindStage, FinalStageImp } from './BuildStage'
import { Provider, Scope } from './Declaration'

export class Container {
  private instances = new Map<string, Object>()
  private providers = new Map<string, Provider<any>>()

  register<T>(token: string, builder: Provider<T>) {
    this.providers.set(token, builder)
  }

  resolve<T>(token: string): T {
    const provider = this.providers.get(token)
    if (!provider?.clazz) {
      throw new Error(`No constructor found for ${token}`)
    }
    const { clazz, scope } = provider
    if (scope === Scope.Singleton) {
      if (this.instances.has(token)) {
        return this.instances.get(token) as T
      }
      const instance = new clazz()
      this.instances.set(token, instance)
      return instance
    }
    return new clazz()
  }

  bind(token: string): BindStage<any> {
    return new FinalStageImp(token, this)
  }

  delete(token: string) {
    this.instances.delete(token)
    this.providers.delete(token)
  }

  clear() {
    this.instances.clear()
    this.providers.clear()
  }
}

export const container = new Container()